
package client;

import java.util.ArrayList;


public class FluxControl extends Thread{

    private ArrayList<Long> timeCountList;
    private ArrayList<Integer> timeOuts;
    private long timeout;
    private long estimatedRTT;
    private long sampleRTT;
    private long devRTT;
    private float alpha, beta;
    private boolean keeprunning;
    private int lostPackets;
    private int threshold;
    private int ackCounter;
    
    
    
    public FluxControl(int pack_num, Long estimatedRTT) {
        this.timeCountList = new ArrayList<Long>();
        this.timeOuts = new ArrayList<Integer>();
        for (int i = 0; i < pack_num + 1; i++) {
            timeCountList.add(Long.valueOf(0));
            timeOuts.add(0);
        }
        this.estimatedRTT = 2 * estimatedRTT;
        this.alpha = (float) 0.125;
        this.beta = (float) 0.25;
        this.keeprunning = true;
        this.lostPackets = 0;
        threshold = 0;
        ackCounter = 0;
        estimateRTT();
        calculateStdDevRTT();
        calculateTimeOut();

    }

    public ArrayList getTimeCountList() {
        ArrayList newList = new ArrayList();

        for (int i = 1; i < timeCountList.size(); i++) {
            newList.add(timeCountList.get(i));
        }

        return newList;
    }

    public synchronized void setTimeCountList(int index, long value) {        
            timeCountList.set(index, value);
        
    }

    
    public void stoprunning() {
        keeprunning = false;
    }

    
    public void newAck(int index) {
        sampleRTT = System.currentTimeMillis() - timeCountList.get(index);
        if (timeOuts.get(index) != -1) {
            
            timeCountList.set(index, Long.valueOf(0));
            Client.getSend().decreaseWindowSize();
            Client.getRecieve().setReceivedAcks(Client.getRecieve().getReceivedAcks() + 1);
            ackCounter++;
            
            if (ackCounter >= Client.getSend().getWindow()) {
                if (threshold > ackCounter || threshold == 0) {
                    Client.getSend().setWindow(ackCounter * 2);
                } else {
                    Client.getSend().setWindow(ackCounter + 1);
                }
                ackCounter = 0;
            }
        }
        estimateRTT();
        calculateStdDevRTT();
        calculateTimeOut();
    }
    
    private void estimateRTT() {
        long newRTT = (long) ((1 - alpha) * estimatedRTT + alpha * sampleRTT);
        estimatedRTT = newRTT;
    }

    private void calculateStdDevRTT() {
        long newdevRTT = (long) ((1 - beta) * devRTT + beta * (Math.abs(sampleRTT - estimatedRTT)));
        devRTT = newdevRTT;
    }

    private void calculateTimeOut() {
        timeout = estimatedRTT + 4 * devRTT;
    }

    @Override
    public void run() {
        Long current, total, time;
        int i;
        while (keeprunning) {
            for (i = 0; i < timeCountList.size(); i++) {
                time = timeCountList.get(i);
                if (time != 0 && timeOuts.get(i) != -1) {
                    current = System.currentTimeMillis();
                    total = current - time;
                    if (total > timeout) {
                        ackCounter=0;
                        lostPackets++;
                        timeOuts.set(i, -1);
                        Client.getSend().setCurrentWindow(
                                Client.getSend().getCurrentWindow() - 1);
                        Client.unpause();
                        threshold = Client.getSend().getWindow() / 2;
                        ackCounter = 0;
                        Client.getSend().setWindow(1);
                        Client.getRecieve().addLoss(i);
                    }
                }
            }
        }
    }
    
    
}